namespace effect {

	let $factories: Map<string, egret.MovieClipDataFactory> = new Map();
	let $datas: Map<string, egret.MovieClipData> = new Map();
	let $changePlay: boolean;

	function _dataKey(options: hjc.McOptions): string { return `${options.res} >>> ${options.mc}`; }

	function _getFactory(options: hjc.McOptions): egret.MovieClipDataFactory {
		if ($factories.has(options.res)) return $factories.get(options.res);
		let factory = new egret.MovieClipDataFactory(RES.getRes(options.res + '_json'), RES.getRes(options.res + '_png'));
		$factories.set(options.res, factory); return factory;
	}

	function _setData(mc: egret.MovieClip, options: hjc.McOptions): egret.MovieClip {
		if ($datas.has(_dataKey(options))) {
			mc.movieClipData = $datas.get(_dataKey(options));
			mc.movieClipData['$resKey'] = _dataKey(options);
		} else {
			let data = _getFactory(options).generateMovieClipData(options.mc);
			if (data.$isDataValid()) {
				$datas.set(_dataKey(options), data);
				mc.movieClipData = data;
				data['$resKey'] = _dataKey(options);
			} else hjc.error('invalid movieclip data:', 'res:' + options.res, 'mc:' + options.mc);
		}
		return mc;
	}

	function _ucMc(options: hjc.McOptions, mc?: egret.MovieClip): egret.MovieClip {
		$changePlay = true;
		if (!!mc) {
			if (!!mc.movieClipData) {
				if (!!options.res || !!options.mc) {
					if (!options.res || !options.mc) {
						let [oriRes, oriMc] = (<string>mc.movieClipData['$resKey']).split(' >>> ');
						if (!options.res) options.res = oriRes;
						if (!options.mc) options.mc = oriMc;
					}
					$changePlay = mc.movieClipData['$resKey'] != _dataKey(options);
					if ($changePlay) return _setData(mc, options);
				} else $changePlay = false;
				return mc;
			} else {
				if (!!options.res && !!options.mc) return _setData(mc, options);
				hjc.warn('both "res" & "mc" required to reset a uninited movieclip!'); return mc;
			}
		} else return _setData(new egret.MovieClip(), options);
	}

	function _set(mc: egret.MovieClip, options: hjc.McOptions): egret.MovieClip {
		mc.touchEnabled = false;
		if (!!options.name) mc.name = options.name;
		if (isBoolean(options.visible)) mc.visible = options.visible;
		if (numeric(options.scale)) {
			mc.scaleX = options.scale;
			mc.scaleY = options.scale;
		}
		if (numeric(options.scaleX)) mc.scaleX = options.scaleX;
		if (numeric(options.scaleY)) mc.scaleY = options.scaleY;
		if (numeric(options.offset)) {
			mc.anchorOffsetX = -options.offset / mc.scaleX;
			mc.anchorOffsetY = -options.offset / mc.scaleY;
		}
		if (numeric(options.offsetX)) mc.anchorOffsetX = -options.offsetX / mc.scaleX;
		if (numeric(options.offsetY)) mc.anchorOffsetY = -options.offsetY / mc.scaleY;
		if (Math.isPositive(options.frameRate)) mc.frameRate = options.frameRate;
		if (Math.isPositive(options.frameRateRatio)) mc.frameRate *= options.frameRateRatio;
		if (numeric(options.alpha)) mc.alpha = options.alpha;
		if (isFunc(options.complete)) mc.addEventListener(egret.Event.COMPLETE, options.complete, this);
		if (isFunc(options.loopComplete)) mc.addEventListener(egret.Event.LOOP_COMPLETE, options.loopComplete, this);
		if (isFunc(options.frameLabel)) mc.addEventListener(egret.MovieClipEvent.FRAME_LABEL, options.frameLabel, this);
		if (!!options.motion) $changePlay = true;
		if ($changePlay) {
			if (options.auto !== false) {
				let times = !!options.loop ? -1 : (Math.isPosInt(options.times) ? options.times : 1);
				options.motion ? mc.gotoAndPlay(options.motion, times) : mc.play(times);
			}
		} else {
			if (isBoolean(options.loop) || Math.isPosInt(options.times)) {
				mc.gotoAndStop(0);
				let times = !!options.loop ? -1 : (Math.isPosInt(options.times) ? options.times : 1);
				options.motion ? mc.gotoAndPlay(options.motion, times) : mc.play(times);
			}
		}
		return mc;
	}

	export function printMcCaches() {
		hjc.info('movieclip factories:', Array.from($factories.keys()));
		hjc.info('movieclip datas:', Array.from($datas.keys()));
	}

	export function createMc(options: hjc.McOptions): egret.MovieClip {
		if (!options.res || !options.mc) { hjc.warn('both "res" & "mc" required to create movieclip!'); return null; }
		return _set(_ucMc(options), options);
	}

	export function resetMc(mc: egret.MovieClip, options: hjc.McOptions): egret.MovieClip {
		if (!mc) { hjc.warn('reset empty movieclip!'); return mc; }
		if (isEmpty(options)) return mc;
		return _set(_ucMc(options, mc), options);
	}

	/**
	 * 1、createMc()调用时必须有res和mc参数。
	 * 2、resetMc()调用时若mc未设置过数据则必须有res和mc参数。
	 * 3、movieclip默认自动播放(除非显示设置auto=false),当设置loop=true时循环,否则播放times次(默认1)。
	 * 4、当含有res或mc或motion参数时(播放变化),先前的loop/times/auto(和播放相关的参数)需要重设。
	*/
}